#include "stdafx.h"
#include "MASTER.h"
#define TECPLOTENGINEMODULE

/*
******************************************************************
******************************************************************
*******                                                   ********
******  (C) 1988-2008 Tecplot, Inc.                        *******
*******                                                   ********
******************************************************************
******************************************************************
*/

#define STRUTILMODULE
#include "GLOBAL.h"
#include "TASSERT.h"
#include "Q_UNICODE.h"
#if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
#endif
#include "ARRLIST.h"
#include "STRLIST.h"
#include "STRUTIL.h"
#include "ALLOC.h"

#include "Q_MSG.h"

#include <algorithm>
#include <cctype> // ...needed to find std::tolower and std::toupper
#include <limits.h>
#include "TranslatedString.h"
#if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
#endif

using namespace std;
using namespace tecplot::strutil;

#ifdef MSWIN
# pragma warning (disable : 4786) /* STL warning about trucated identifiers */
#endif

/* END HEADER */

/**
 */
#define           INITIAL_FORMAT_BUFFER_SIZE 16384*3
#if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
#endif
static char      *FormatStringBuffer = NULL;
static LgIndex_t  FormatStringBufferSize = INITIAL_FORMAT_BUFFER_SIZE;

#if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
#if defined MSWIN
#else
#endif /* !MSWIN */
#endif

#if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
#endif

/**
 * This should be one of the last functions called by Tecplot while mopping up.
 */
void FormatStringBufferCleanup(void)
{
# if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
# endif

  /*
   * NOTE: We use free instead of FREE_ARRAY for the scratch buffer because in
   *       debug mode FREE_ARRAY uses ErrMsg which uses vFormatString causing
   *       infinite recursion.
   */
  if (FormatStringBuffer != NULL)
    free(FormatStringBuffer);
  FormatStringBuffer = NULL;

# if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
# endif
}

/**
 */
char *vFormatString(const char *Format,
                    va_list     Arguments)
{
  char *Result = NULL;

  REQUIRE(VALID_REF(Format));

  #if defined TECPLOTKERNEL
    {
      /*
       * Allow use of FormatString even before threading is initialized.
       * FormatString will not be thread-safe at those times, but there
       * should be no threading. 2006-12-08
       */
      if (Thread_ThreadingIsInitialized())
        Thread_LockMutex(FormatStringBufferLock);
    }
  #endif

  /*
   * NOTE: We use malloc instead of ALLOC_ARRAY for the scratch buffer because
   *       in debug mode ALLOC_ARRAY uses ErrMsg which uses vFormatString
   *       causing infinite recursion.
   */
  if (FormatStringBuffer == NULL)
    FormatStringBuffer = (char *)malloc(FormatStringBufferSize);

  if (FormatStringBuffer != NULL)
    {
      Boolean_t TryAgain = FALSE;
      do
        {
          /*
           * Assign a value other than '\0' to the end of the buffer so that we
           * can determine if the buffer needs to be expanded. If after we call
           * vsnprintf the end of the buffer has a '\0' we need to expand it.
           */
          FormatStringBuffer[FormatStringBufferSize - 1] = (char)!'\0';

#         if defined MSWIN
          memset(FormatStringBuffer,0,FormatStringBufferSize-1);

          TryAgain = 
            _vsnprintf(FormatStringBuffer,
                       FormatStringBufferSize,
                       Format,
                       Arguments) == -1;
#         elif defined IRIX62
            vsprintf(FormatStringBuffer,
                     Format,
                     Arguments);
            CHECK(strlen(FormatStringBuffer) < FormatStringBufferSize);
#         else
            vsnprintf(FormatStringBuffer,
                      FormatStringBufferSize,
                      Format,
                      Arguments);
#         endif

#ifndef MSWIN
          TryAgain = (FormatStringBuffer[FormatStringBufferSize - 1] == '\0');
#endif
          if (TryAgain)
            {
              /*
               * Reallocate the buffer and try again.
               *
               * NOTE: We use malloc/free instead of ALLOC/FREE_ARRAY for the
               *       scratch buffer because in debug mode ALLOC/FREE_ARRAY
               *       uses ErrMsg which uses vFormatString causing infinite
               *       recursion.
               */
              free(FormatStringBuffer);
              FormatStringBufferSize += MAX(1, FormatStringBufferSize / 2);
              FormatStringBuffer = (char *)malloc(FormatStringBufferSize);
              TryAgain = (FormatStringBuffer != NULL);
              if (!TryAgain)
                FormatStringBufferSize = INITIAL_FORMAT_BUFFER_SIZE;
            }
        } while (TryAgain);

      if (FormatStringBuffer != NULL)
        Result = DupString(dontTranslate(FormatStringBuffer));
    }

  #if defined TECPLOTKERNEL
    {
      if ( Thread_ThreadingIsInitialized() )
        Thread_UnlockMutex(FormatStringBufferLock);
    }
  #endif

  ENSURE(VALID_REF(Result) || Result == NULL);
  return Result;
}

/**
 */
char *FormatString(TranslatedString Format,
                   ...) /* 0 or more variable arguments */
{
  REQUIRE(!Format.isNull());

  va_list Arguments;
  va_start(Arguments, Format);
  char *Result = vFormatString(Format.c_str(), Arguments);
  va_end(Arguments);

  ENSURE(VALID_REF(Result) || Result == NULL);
  return Result;
}

/**
 */
int FormatString(string&           Buffer,
                 TranslatedString  Format
                 ...) /* 0 or more variable arguments */
{
  REQUIRE(!Format.isNull());

  va_list Arguments;
  va_start(Arguments, Format);
  char *FormattedString = vFormatString(Format.c_str(), Arguments);
  va_end(Arguments);

  int Result;
  if (FormattedString != NULL)
    {
      Buffer.assign(FormattedString);
      Result = (int)Buffer.size();
      FREE_ARRAY(FormattedString, "FormattedString");
    }
  else
    Result = -1;


  ENSURE(Result == -1 || Result >= 0);
  return Result;
}

/**
 * Returns a duplicate of the string or NULL if sufficient memory is not
 * available.
 *
 * NOTE: This function was created because ResetString(...) does not
 *       duplicate zero length strings but returns NULL instead.
 */
char *DupString(TranslatedString String)
{
  REQUIRE(VALID_TRANSLATED_STRING(String));

  char *Result = ALLOC_ARRAY(strlen(String.c_str()) + 1, char, "duplicate string");
  if (Result != NULL)
    strcpy(Result, String.c_str());

  ENSURE(Result == NULL || (VALID_REF(Result) && strcmp(Result, String.c_str()) == 0));
  return Result;
}


/* 
 * Copy up to 'Count' characters from the 'Source' string beginning at
 * position 'Index' to the 'Target' string. The actual number of characters
 * copied may be less than 'Count' if a '\0' was encountered in the
 * 'Source' string before 'Count' characters were copied.
 *
 * NOTE: The 'Target' and 'Source' strings may overlap.
 */
void CopySubString(char       *Target,
                   const char *Source,
                   LgIndex_t   Index,
                   LgIndex_t   Count)
{
  LgIndex_t Length = 0;

  REQUIRE(VALID_REF(Target));
  REQUIRE("Target string is sized to accommodate a string who's length "
          "is at least MIN(strlen(&Source[Index]), Count) characters.");
  REQUIRE(VALID_REF(Source));
  REQUIRE(0 <= Index && Index <= (LgIndex_t)strlen(Source));
  REQUIRE(Count >= 0);

  Length = MIN((LgIndex_t)strlen(&Source[Index]), Count);
  memmove(Target, &Source[Index], Length);
  Target[Length] = '\0'; 

  ENSURE(VALID_REF(Target) && (LgIndex_t)strlen(Target) == Length);
}

#if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
#endif /* TECPLOTKERNEL */

/*
 * Remove any leading white space from the string and return
 * a reference to it. NOTE: The input string is modified.
 */
char *StringFlushLeft(char *String)
{
  char *Result = String;
  char *Start = String;

  REQUIRE(VALID_REF(String));

  /* move the substring beginning at the first non-whitespace */
  /* character to the head of the string                      */
  while (isspace(*Start))
    Start++;
  if (Start != String)
    memmove(String, Start, strlen(Start) + 1);

  ENSURE(VALID_REF(Result) && Result == String);
  return Result;
}


/*
 * Remove any trailing white space from the string and return
 * a reference to it. NOTE: The input string is modified.
 */
static char *StringFlushRight(char *String)
{
  char *Result = String;
  char *End = NULL;
  
  REQUIRE(VALID_REF(String));

  for (End = EndOfString(String); End != String && isspace(End[-1]); End--)
    End[-1] = '\0';

  ENSURE(VALID_REF(Result) && Result == String);
  return Result;
}


/*
 * Remove any leading and trailing white space from the string 
 * and return a reference to it.  The return value is not
 * absolutely necessary since the input string is modified
 * but it is convenient sometimes.
 * NOTE: The input string is modified but no memory is
 * allocated nor deallocated.
 */
char *TrimLeadAndTrailSpaces(char *String)
{
  REQUIRE((String == NULL) || VALID_REF(String));
  if (String)
    return (StringFlushLeft(StringFlushRight(String)));
  else
    return String;
}


/*
 * If the specified string is longer than the maximum specified length
 * truncate it and return a reference to it.
 *
 * String
 *   String to truncate if necessary.
 * MaxLength
 *   Length at which to truncate the specified string if exceeded.
 *
 * Return
 *   Reference to the input string.
 */

// Okay for UTF-8
char *StringTruncate(char      *String,
                     LgIndex_t MaxLength)
{
  REQUIRE(VALID_REF(String));
  REQUIRE(MaxLength >= 0);

  if ((LgIndex_t)strlen(String) > MaxLength)
    String[MaxLength] = '\0';/* UTF8_SetAt(String,'\0',MaxLength); */

  ENSURE(VALID_REF(String));
  ENSURE((LgIndex_t)strlen(String) <= MaxLength);
  return String;
}


/*
 * Trim and truncate the specified string such that it's trimmed length
 * does not exceed the specified length and return a reference to it.
 *
 * String
 *   String to trim and truncate if necessary.
 * MaxLength
 *   Length at which to truncate the trimmed string if exceeded.
 *
 * Return
 *   Reference to the input string.
 */
char *StringTrimAndTruncate(char      *String,
                            LgIndex_t MaxLength)
{
  REQUIRE(VALID_REF(String));
  REQUIRE(MaxLength >= 0);

  TrimLeadAndTrailSpaces(String);
  StringTruncate(String, MaxLength);

  ENSURE(VALID_REF(String));
  ENSURE((LgIndex_t)strlen(String) <= MaxLength);
  return String;
}

/**
 */

#ifndef MSWIN
StringList_pa LineBreakString(const char *String,
                              UInt32_t    WrapMargin)
{
  REQUIRE(VALID_REF(String));

  StringList_pa Result = StringListAlloc();
  if (Result != NULL)
    {
      Boolean_t IsOk = TRUE;
      if (strlen(String) > WrapMargin)
        {
          char *StringCopy = DupString(dontTranslate(String));
          IsOk = (StringCopy != NULL);
          if (IsOk)
            {
              char *CPtr = StringCopy;
              char *SubString = StringCopy;
              UInt32_t SubStringLen = 0;
              while (*CPtr != '\0' && IsOk)
                {
                  while (*CPtr != '\0' && SubStringLen < WrapMargin)
                    {
                      /* look for a hard break */
                      if (*CPtr == '\n')
                        {
                          *CPtr = '\0'; /* replace the newline */
                          CPtr++;
                          break;
                        }

                      CPtr++;
                      SubStringLen++;
                    }

                  /*
                   * If we didn't find a hard break or the end of the string
                   * then we need to back up and find the closest space.
                   */
                  if (*CPtr != '\0' && SubStringLen == WrapMargin)
                    {
                      /* find the closes space from the right */
                      if (*CPtr != ' ')
                        {
                          while (CPtr != SubString && *CPtr != ' ')
                            CPtr--;
                          if (*CPtr != ' ')
                            {
                              /*
                               * Bummer, this line will exceed the wrap margin.
                               * Search forward for the next space or newline.
                               */
                              while (*CPtr != '\0' && *CPtr != ' ' && *CPtr != '\n')
                                CPtr++;
                              while (*CPtr != '\0' && *CPtr == ' ')
                                CPtr++; /* skip over the white space */
                            }
                        }

                      if (*CPtr != '\0')
                        {
                          *CPtr = '\0';
                          CPtr++;
                        }
                      StringFlushRight(SubString);
                    }

                  IsOk = StringListAppendString(Result, SubString);
                  SubString = CPtr;
                  SubStringLen = 0;
                }

              FREE_ARRAY(StringCopy, "StringCopy");
            }
        }
      else
        IsOk = StringListAppendString(Result, String);

      if (!IsOk)
        StringListDealloc(&Result);
    }

  ENSURE(Result == NULL || VALID_REF(Result));
  return Result;
}
#endif


#if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
#endif /* TECPLOTKERNEL */


/**
 * Lexicographically compares, at most, the first 'Len' characters of
 * s1 and s2.
 *
 * param s1
 *     First string or NULL.
 * param s2
 *     Second string or NULL.
 * param Len
 *     Maximum number of characters to compare.
 * return
 *     Integer value greater than, equal to, or less than zero according
 *     to whether the first 'Len' characters of 's1' are greater than,
 *     equal to, or less than 's2'.
 */

// Okay for UTF-8
int ustrncmp(const  char *s1,
             const  char *s2,
             size_t Len)
{
  REQUIRE((s1 == NULL) || VALID_REF(s1));
  REQUIRE((s2 == NULL) || VALID_REF(s2));
  REQUIRE(Len >= 0);

  char *t1;
  char *t2;
  char ct1;
  char ct2;
  size_t I = 0;
  if ((s1 == NULL) && (s2 == NULL))
    return 0;
  if (s1 == NULL)
    return -1;
  else if (s2 == NULL)
    return 1;

  t1 = (char*)s1;
  t2 = (char*)s2;

  while (*t1 && *t2 && (I < Len))
    {
      ct1 = CAPITAL(*t1);
      ct2 = CAPITAL(*t2);
      if (ct1 != ct2)
        return (ct1 - ct2);
      t1++;
      t2++;
      I++;
    }
  if ((I == Len) ||
      ((*t1 == '\0') && (*t2 == '\0')))
    return 0;
  else
    return CAPITAL(*t1) - CAPITAL(*t2);


}


/**
 * Lexicographically compares the characters of s1 and s2.
 *
 * param s1
 *     First string or NULL.
 * param s2
 *     Second string or NULL.
 * return
 *     Integer value greater than, equal to, or less than zero according to
 *     whether the characters of 's1' are greater than, equal to, or less
 *     than 's2'.
 */
int ustrcmp(const char *s1,
            const char *s2)
{
  REQUIRE((s1 == NULL) || VALID_REF(s1));
  REQUIRE((s2 == NULL) || VALID_REF(s2));

  return (ustrncmp(s1,s2,INT_MAX));
}



#if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
#if !defined NO_ASSERTS && defined DEBUG_ALLOC
#endif
#endif /* TECPLOTKERNEL */


/*
 * The problem with passing file names for release builds is that
 * the full path name is used (i.e., c:\user\craig\v7.5\tecplot\alloc.c)
 */

// Okay for UTF-8

#if !defined NO_ASSERTS
  Boolean_t InternalResetString(char       **SBase,
                                const char  *NewString,
                                Boolean_t    IssueErrMsg,
                                const char  *FileName,
                                int          LineNumber)
#else
  Boolean_t InternalResetString(char       **SBase,
                                const char  *NewString,
                                Boolean_t    IssueErrMsg)
#endif
{
  REQUIRE(VALID_REF(SBase));
  REQUIRE(*SBase == NULL || VALID_REF(*SBase));
  REQUIRE(NewString == NULL || VALID_REF(NewString));
  REQUIRE(IMPLICATION(VALID_REF(*SBase), *SBase != NewString)); /* Prevent calling with same string. */
  REQUIRE(VALID_BOOLEAN(IssueErrMsg));
  REQUIRE(VALID_NON_ZERO_LEN_STR(FileName));
  REQUIRE(LineNumber >= 1);

  if (*SBase)
    {
#if !defined NO_ASSERTS && defined DEBUG_ALLOC
      char S[80+1];
      MakeDebugRecord(FileName, LineNumber, "releasing", *SBase, S, 80);
      FREE_ARRAY(*SBase,S);
#else
      FREE_ARRAY(*SBase,"");
#endif
    }
  if ( NewString == NULL )
    {
      *SBase = NULL;
      return (TRUE);
    }
  else
    {
#if !defined NO_ASSERTS && defined DEBUG_ALLOC
      char S[80+1];
      MakeDebugRecord(FileName, LineNumber, "duplicating", NewString, S, 80);
      *SBase = ALLOC_ARRAY(strlen(NewString)+1,char,S);
#else
#  if defined MSWIN && defined _DEBUG && !defined(MAKEARCHIVE) && !defined(NO_ASSERTS)
      /* Allow the MFC memory leak detection to report the leak at the
       * calling programs location, and not here (which is fairly useless).
       * But first, we have to turn off the preprocessor definition of new
       * and get to the original. */
#     undef new
        *SBase = new(FileName, LineNumber) char[strlen(NewString)+1];
#     define new DEBUG_NEW
#  else
      *SBase = ALLOC_ARRAY(strlen(NewString)+1,char,"");
#  endif
#endif
      if (*SBase)
        {
          strcpy(*SBase,NewString);
          return (TRUE);
        }
      else
        {
          if (IssueErrMsg)
            ErrMsg(translate("Out of memory"));
          return (FALSE);
        }
    }
}


#if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
#endif



/*
 * Unfortunately, this routine uses the interface of DeleteStringToAdd
 * forcing StringToAdd to be non-const.  Another copy of this routine
 * for const char *'s is below.  Eventually we should get rid of
 * the two routines and DeleteStringToAdd, always using the const version
 * and deleting the string if necessary in the code that calls TackOnString.
 */
Boolean_t TackOnString(char       **SBase,
                       const char  *StringToAdd,
                       Boolean_t    DeleteStringToAdd,
                       Boolean_t    ConvertNewlinesToAscii)
{
  size_t      CurLen;
  size_t      NewLen;
  int         NumNewlines = 0;
  char       *NewString;
  const char *CPtr = StringToAdd;
  Boolean_t   IsOk = TRUE;

  REQUIRE(VALID_REF(SBase));
  REQUIRE((StringToAdd == NULL) || VALID_REF(StringToAdd));
  REQUIRE(VALID_BOOLEAN(DeleteStringToAdd));
  REQUIRE(VALID_BOOLEAN(ConvertNewlinesToAscii));

  if ((StringToAdd  == NULL) ||
      (*StringToAdd == '\0'))
    {      
      if (StringToAdd            && 
          (*StringToAdd == '\0') && 
          DeleteStringToAdd)
        {
          char *TMP = (char *)StringToAdd;
          FREE_ARRAY(TMP,"empty string to add");
        }
    }
  else
    {

      if (*SBase == NULL)
        CurLen = 0;
      else
        CurLen = strlen(*SBase);
    
      while (*CPtr)
        if (*CPtr++ == '\n')
          NumNewlines++;
    
      NewLen = CurLen + strlen(StringToAdd) + 1 + NumNewlines;
    
      NewString = ALLOC_ARRAY(NewLen,char,StringToAdd);
    
      if (NewString == NULL)
        {
          if (DeleteStringToAdd)
            {
              char *TMP = (char *)StringToAdd;
              FREE_ARRAY(TMP,StringToAdd);
            }            
          IsOk = FALSE;
        }
      else
        {
          if (*SBase)
            {
              strcpy(NewString,*SBase);
              FREE_ARRAY(*SBase,(CurLen > 0 ? *SBase : "previous text"));
            }
          else
            *NewString = '\0';
        
          {
            char *NPtr = EndOfString(NewString);
            const char *APtr = StringToAdd;
            while (*APtr)
              {
                if ((*APtr == '\n') && ConvertNewlinesToAscii)
                  {
                    *NPtr++ = '\\';
                    *NPtr++ = 'n';
                  }
                else
                  *NPtr++ = *APtr; //UTF8_AssignAndIncrement(&NPtr,(char**)&APtr,TRUE,FALSE); //*NPtr++ = *APtr;
                APtr++;
              }
            *NPtr = '\0';
          }
        
          if (DeleteStringToAdd)
            {
              char *TMP = (char *)StringToAdd;
              FREE_ARRAY(TMP,StringToAdd);
            }
        
          *SBase = NewString;
       }
   }

  ENSURE (VALID_BOOLEAN(IsOk));
  return (IsOk);   
}


/*
 * See TackOnString for discussion.
 */

// Okay for UTF-8
Boolean_t TackOnConstString(char      **SBase,
                            const char *StringToAdd,
                            Boolean_t   ConvertNewlinesToAscii)
{
  size_t      CurLen;
  size_t      NewLen;
  int         NumNewlines = 0;
  char       *NewString;
  const char *CPtr = StringToAdd;
  Boolean_t   IsOk = TRUE;

  REQUIRE(VALID_REF(SBase));
  REQUIRE((StringToAdd == NULL) || VALID_REF(StringToAdd));
  REQUIRE(VALID_BOOLEAN(ConvertNewlinesToAscii));

  if ( (StringToAdd  != NULL) &&
       (*StringToAdd != '\0') )
    {
      if (*SBase == NULL)
        CurLen = 0;
      else
        CurLen = strlen(*SBase);
    
      while (*CPtr)
        if (*CPtr++ == '\n')
          NumNewlines++;
    
      NewLen = CurLen + strlen(StringToAdd) + 1 + NumNewlines;
    
      NewString = ALLOC_ARRAY(NewLen,char,StringToAdd);
    
      if (NewString == NULL)
        {
          IsOk = FALSE;
        }
      else
        {
          if (*SBase)
            {
              strcpy(NewString,*SBase);
              FREE_ARRAY(*SBase,(CurLen > 0 ? *SBase : "previous text"));
            }
          else
            *NewString = '\0';
        
          {
            char *NPtr = EndOfString(NewString);
            const char *APtr = StringToAdd;
            while (*APtr)
              {
                if ((*APtr == '\n') && ConvertNewlinesToAscii)
                  {
                    *NPtr++ = '\\';
                    *NPtr++ = 'n';
                  }
                else
                  *NPtr++ = *APtr; // UTF8_AssignAndIncrement(&NPtr,(char**)&APtr,TRUE,FALSE);
                APtr++;
              }
            *NPtr = '\0';
          }
          *SBase = NewString;
       }
   }

  ENSURE (VALID_BOOLEAN(IsOk));
  return (IsOk);   
}


// Okay for UTF-8
Boolean_t TackOnChar(char  **SBase,
                     char    CharToAdd)
{
  REQUIRE(VALID_REF(SBase));

  char S[2];
  S[0] = CharToAdd;
  S[1] = '\0';

  return (TackOnString(SBase,S,FALSE,FALSE));
}



/**
 * Converts all one character new line characters in the allocated string
 * to a two character "\n" sequence.
 *
 * param String
 *     String to scan and convert if necessary. Note that the string will
 *     be reallocated if any new line characters are discovered.
 *
 * return
 *     TRUE if the request was successfull, FALSE otherwise.
 */

// Okay for UTF-8
Boolean_t ReplaceNewlineWithBackslashN(char **String)
{
  size_t    I;
  LgIndex_t NewlineCount;
  size_t    Length;
  char     *Replacement;

  REQUIRE(VALID_REF(String));
  REQUIRE(VALID_REF(*String));

  /* count how many new line character are present */
  NewlineCount = 0;
  Length = strlen(*String);
  for (I = 0; I < Length; I++)
    if ((*String)[I] == '\n')
      NewlineCount++;

  if (NewlineCount != 0)
    {
      /* allocate a new string and convert */
      Replacement = ALLOC_ARRAY(Length + NewlineCount + 1, char,
                                "replacement string");
      if (Replacement != NULL)
        {
          size_t J;
          for (I = J = 0; I < Length + 1; I++, J++)
            {
              if ((*String)[I] == '\n')
                {
                  Replacement[J] = '\\';
                  J++;
                  Replacement[J] = 'n';
                }
              else
                {
                  Replacement[J] = (*String)[I];
                }
            }

          /* sanity check */
          CHECK(I == Length + 1);
          CHECK(J == Length + NewlineCount + 1);
        }

      /* release the old string and record the new one */
      FREE_ARRAY(*String, "original string");
      *String = Replacement;
    }

  ENSURE(*String == NULL || VALID_REF(*String));
  return (*String != NULL);
}

#if defined TECPLOTKERNEL
/* CORE SOURCE CODE REMOVED */
#if defined TECPLOTKERNEL
#if !defined NO_ASSERTS
#endif /* !NO_ASSERTS */
#endif /*TECPLOTKERNEL*/
#endif
